/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package fsktms.packetmanager;

import fsktms.IPacket;
import java.io.Closeable;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author salm
 */
class PacketInteractor
    implements Runnable, Closeable
{
    private static int TIME_OUT = 500;
    
    private static int packageSizeType = 4;
    private static int waitTime = 10;
    
    private static enum WAIT_STATE {WAIT_SIGNAL, WAIT_MSG, OK};
    
    private WAIT_STATE state = WAIT_STATE.WAIT_SIGNAL;
    private int msgSize = 0;
    
    private final Socket client;
    private IPacket pkg;
    
    private boolean closed = false;
    
    private String address;
    
    private DataInputStream bis;
    private DataOutputStream bos;
    
    public PacketInteractor(Socket cl) throws IOException
    {
        this.client = cl;
        this.bis = new DataInputStream(client.getInputStream());
        bos = new DataOutputStream(client.getOutputStream());
        this.address = (this.client.getInetAddress().getHostAddress() + "::" + this.client.getRemoteSocketAddress());
        
        (new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                while (!isClosed())
                {
                    checkClosed();
                    try
                    {
                        Thread.sleep(TIME_OUT);
                    }
                    catch (InterruptedException ex)
                    {
                    }
                }
            }
        })).start();
    }
    
    @Override
    public void run()
    {
        try
        {
            if (!this.isClosed())
            {
                while (!this.isClosed() && this.state != WAIT_STATE.WAIT_SIGNAL)
                {
                    Thread.sleep(waitTime);
                }
                
                this.state = WAIT_STATE.WAIT_SIGNAL;
                while (!this.isClosed() && bis.available() < packageSizeType)
                {
                    Thread.sleep(waitTime);
                }
                
                if (!this.isClosed())
                {
                    this.msgSize = Integer.reverseBytes(bis.readInt());
                }
                this.state = WAIT_STATE.WAIT_MSG;
                while (!this.isClosed() && bis.available() < this.msgSize)
                {
                    Thread.sleep(waitTime);
                }
                
                if (!this.isClosed())
                {
                    int indx = Integer.reverseBytes(bis.readInt());
                    int type = Integer.reverseBytes(bis.readInt());
                    if (type != 4) // hard code for ping
                    {
                        byte[] data = new byte[msgSize - additionalSize];
                        bis.read(data);

                        System.out.println("Received a package from " + this.getAddress() + "\ttype: " + type + "\tindex: " + indx + "\tdataLen: " + data.length);
                        this.pkg = new Packet(type, indx, data);
                        this.state = WAIT_STATE.OK;
                    }
                    else
                    {
                        this.state = WAIT_STATE.WAIT_SIGNAL;
                    }
//                    this.SendPkg(PacketFactory.createOKPacket());
                }
            }
        }
        catch (InterruptedException | IOException e)
        {
            try
            {
                this.close();
            } catch (IOException ex)
            {
                Logger.getLogger(PacketInteractor.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
    
    public boolean isHasPkg()
    {
        return this.state == WAIT_STATE.OK;
    }
    
    public IPacket GetPkg()
    {
        IPacket res = this.pkg;
        this.pkg = null;
        this.state = WAIT_STATE.WAIT_SIGNAL;
        return res;
    }
    
    private static int additionalSize = 8;
    public synchronized void SendPkg(IPacket pkg) throws IOException
    {
        //4 is ping, we can die if its changes
        if (pkg.getType() == 4 || !this.isClosed())
        {
            bos.writeInt(Integer.reverseBytes(pkg.getData().length + additionalSize));
            bos.writeInt(Integer.reverseBytes(pkg.getIndex()));
            bos.writeInt(Integer.reverseBytes(pkg.getType()));
            bos.write(pkg.getData());
            bos.flush();
        }
    }
    
    @Override
    public void close() throws IOException
    {
        this.closed = true;
        this.client.close();
    }
    
    public boolean isClosed()
    {
        return this.closed;
    }
    
    public String getAddress()
    {
        return address;
    }
    
    private void checkClosed()
    {
        try
        {
            this.SendPkg(PacketFactory.createPingPacket());
        } catch (IOException ex)
        {
            try
            {
                this.close();
            }
            catch (IOException ex1)
            {
            }
        }
    }
}
